package com.lingjtx.common.security.strategy;

import com.lingjtx.common.core.RedisOptions;
import com.lingjtx.common.core.ResponseCode;
import com.lingjtx.common.core.ResponseResult;
import com.lingjtx.common.core.algorithm.RSAUtil;
import com.lingjtx.common.core.constant.Const;
import com.lingjtx.common.core.constant.ResponseTip;
import com.lingjtx.common.core.handle.ObjectPool;
import com.lingjtx.common.core.token.UserVo;
import com.lingjtx.common.core.util.StringUtil;
import com.lingjtx.common.other.helper.MessageManager;
import com.lingjtx.common.other.helper.WeiXinOauth;
import com.lingjtx.common.other.http.Call;
import com.lingjtx.common.other.http.Network;
import com.lingjtx.common.other.http.RemoteService;
import com.lingjtx.common.other.model.jiguang.LoginTokenRequest;
import com.lingjtx.common.other.model.jiguang.LoginTokenResponse;
import com.lingjtx.common.other.model.weixin.OauthResponse;
import com.lingjtx.common.core.annotation.IgnoreLogin;
import com.lingjtx.common.core.token.IgnoreType;
import com.lingjtx.common.core.token.LoginMode;
import com.lingjtx.common.security.model.LoginPo;
import com.lingjtx.common.security.limit.HandlerState;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.net.http.HttpClient;
import java.util.Objects;

@Component
public class LoginPolicy extends HandlerState<ProceedingJoinPoint, HttpServletRequest> {

    private ResponseResult<Object> responseResult;

    private LoginCallback callback;

    public ResponseResult<Object> getResponseResult() {
        return responseResult;
    }

    public void setResponseResult(ResponseResult<Object> responseResult) {
        this.responseResult = responseResult;
    }

    public LoginCallback getCallback() {
        return callback;
    }

    public void setCallback(LoginCallback callback) {
        this.callback = callback;
    }

    @Override
    public ResponseResult<Object> retError() {
        return getResponseResult();
    }

    @Override
    public void handler(ProceedingJoinPoint point, HttpServletRequest request, RedisOptions redis) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        IgnoreLogin annotation = signature.getMethod().getAnnotation(IgnoreLogin.class);
        if (Objects.nonNull(annotation) && IgnoreType.LOGIN.equals(annotation.value())) {
            Object[] args = point.getArgs();
            if (args[0] instanceof LoginPo loginPo && Objects.nonNull(callback)) {
                if (!LoginMode.REFRESH_TOKEN.equals(annotation.model()) // 如果是刷新token操作就不判断登录环境参数
                        && (StringUtil.isEmpty(loginPo.getDeviceModel())
                        || StringUtil.isEmpty(loginPo.getDeviceUid())
                        || loginPo.getOperationDevice() == null)) {
                    super.setErrorState(true);
                    this.setResponseResult(ResponseResult.createByError("登录时加上详情环境参数！"));
                    return;
                }
                if (LoginMode.EMAIL.equals(annotation.model()) || LoginMode.MOBILE.equals(annotation.model())) { // 检测参数和验证 验证码
                    if (StringUtil.isEmpty(loginPo.getIdentifier()) || StringUtil.isEmpty(loginPo.getSmsCode())) {
                        super.setErrorState(true);
                        this.setResponseResult(ResponseResult.createByError(ResponseCode.PARAM_IS_NULL));
                        return;
                    }
                    if (!MessageManager.INSTANCE.valid(loginPo.getIdentifier(), loginPo.getSmsCode(), redis)) {
                        super.setErrorState(true);
                        this.setResponseResult(ResponseResult.createByError(ResponseTip.RESPONSE_TIP_SMS_CODE_ERROR));
                        return;
                    }
                }
                // 检查新注册用户。登录规则：手机号码主登录，微信和人脸要绑定手机号码。如果手机号码已经存（已注册），改人脸和微信id绑在该手机号码注册的账号下
                if (LoginMode.FACE.equals(annotation.model())) { // 人脸登录
                    UserVo faceUser = callback.selectUserByIdentifier(loginPo.getIdentifier());
                    // 如果userId为空，那么要绑定手机号码
                    if (faceUser == null) {
                        super.setErrorState(true);
                        this.setResponseResult(ResponseResult.createByError(ResponseCode.UNBOUND_MOBILE, loginPo.getIdentifier())); // 人脸id也一起返回
                        return;
                    }
                }
                if (LoginMode.WX_OPENID.equals(annotation.model())) { // 微信授权登录
                    OauthResponse oauthResponse = WeiXinOauth.INSTANCE.getOauthAccessToken(loginPo.getIdentifier(), loginPo.getSmsCode(), redis, callback.getObjectPool());
                    if (Objects.isNull(oauthResponse) || StringUtil.isEmpty(oauthResponse.getOpenid())) {
                        super.setErrorState(true);
                        this.setResponseResult(ResponseResult.createByError("微信授权登录失败！"));
                        return;
                    }
                    // 微信用户唯一标识查询是否注册
                    UserVo wxUser = callback.selectUserByIdentifier(oauthResponse.getOpenid());
                    // 如果userId为空，那么要绑定手机号码
                    if (wxUser == null) {
                        // 拉起用户微信信息，存储到缓存，在用户下一步提交注册信息的时候获取
                        WeiXinOauth.INSTANCE.saveUserInfo(oauthResponse.getAccess_token(), oauthResponse.getOpenid(), redis, callback.getObjectPool());
                        loginPo.setWxOpenid(oauthResponse.getOpenid());
                        super.setErrorState(true);
                        this.setResponseResult(ResponseResult.createByError(ResponseCode.UNBOUND_MOBILE, oauthResponse.getOpenid())); // 微信openid也一起返回
                        return;
                    }
                    loginPo.setIdentifier(oauthResponse.getOpenid()); // 获取到的微信openid替换掉微信code
                }
                if (LoginMode.ONE_CLICK.equals(annotation.model())) {
                    RemoteService service = Network.remote(RemoteService.class, callback.getObjectPool());
                    Call<LoginTokenResponse> call = service.loginTokenVerify(new LoginTokenRequest(loginPo.getIdentifier()));
                    LoginTokenResponse result = call.execute();
                    if (result == null) {
                        super.setErrorState(true);
                        this.setResponseResult(ResponseResult.createByError(ResponseTip.RESPONSE_TIP_LOGIN_FAIL));
                        return;
                    }
                    if (Const.JiGangCode.SUCCESS == result.getCode()) {
                        String mobile = RSAUtil.decryptByDefaultKey(result.getPhone());
                        loginPo.setIdentifier(mobile); // 极光一键登录获取到的手机号码替换之前的极光登录验证token
                    }
                }

                UserVo user = callback.selectUserByIdentifier(loginPo.getIdentifier());
                // 账号登录注册
                callback.register(annotation.model(), loginPo, user, request);
            }
        }
    }

    public interface LoginCallback {
        UserVo selectUserByIdentifier(String uid);

        void register(LoginMode model, LoginPo loginPo, UserVo user, HttpServletRequest request);

        ObjectPool<HttpClient> getObjectPool();
    }
}
